﻿using System;
using System.Threading;
using System.Timers;

namespace ShareHelper
{
    /// <summary>
    /// 不可重入的定时器
    /// </summary>

    public class TimerEx : IDisposable
    {
        /// <summary>
        /// 定时器
        /// </summary>
        private System.Timers.Timer Time { get; set; }

        /// <summary>
        ///创建一个不可重入的定时器
        /// </summary>
        public TimerEx()
        {
            var t = new System.Timers.Timer
            {
                AutoReset = false
            };
            t.Elapsed += OnElapsed;
            Time = t;
        }

        /// <summary>
        ///创建一个不可重入的定时器
        /// </summary>
        /// <param name="dueTime">多久之后开始。毫秒</param>
        /// <param name="period">间隔周期。毫秒</param>
        public TimerEx(int dueTime, int period) : this()
        {
            DueTime = dueTime;
            Period = period;
        }

        /// <summary>
        /// 创建一个不可重入的定时器
        /// </summary>
        /// <param name="period">间隔周期。毫秒</param>
        public TimerEx(int period) : this(period, period)
        {
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="state">用户数据</param>
        /// <param name="dueTime">多久之后开始。毫秒</param>
        /// <param name="period">间隔周期。毫秒</param>
        public TimerEx(object state, int dueTime, int period) : this(dueTime, period) => State = state;

        /// <summary>
        ///获取/设置 用户数据
        ///</summary>
        public object State { get; set; }

        /// <summary>
        /// 获取/设置 第一次调用时间
        /// </summary>
        public int DueTime { get; set; } = 100;

        /// <summary>
        /// 获取/设置 间隔周期。毫秒，设小于或等于0 则只调用一次
        /// </summary>
        public int Period { get; set; }

        /// <summary>
        /// 如果  应引发 Elapsed 事件，则为 true；否则，为 false。 默认值为 false。
        /// </summary>
        public bool Enabled => Time != null && Interlocked.CompareExchange(ref enabled, 1, 1) == 1;

        /// <summary>
        /// 如果  应引发 Elapsed 事件，则为 1；不引发则，为 0。已释放为-1 默认值为 0。
        /// </summary>
        private int enabled;

        /// <summary>
        /// 达到间隔时发生。
        /// </summary>
        public event WaitCallback Elapsed;

        /// <summary>
        /// 判断任务是否执行的委托。一般跟异步配合使用，避免频繁从线程池借出线程
        /// </summary>
        public Func<bool> CanExecute { get; set; }

        /// <summary>
        /// 内部回调事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnElapsed(object sender, ElapsedEventArgs e)
        {
            if (!Enabled) return;
            bool canExe = true;
            try
            {
                Elapsed?.Invoke(State);
                canExe = CanExecute?.Invoke() != false;
            }
            catch (Exception exce) { Log.Error($"{nameof(TimerEx)}-OnElapsed", exce); }
            finally
            {
                if (Enabled && canExe)//引发事件
                {
                    int period = Period;
                    if (period > 0) Start(period);//重复引发
                    else//不重复  事件关闭
                        Interlocked.Exchange(ref enabled, 0);
                }
                else//事件关闭
                    Interlocked.Exchange(ref enabled, 0);
            }
        }

        /// <summary>
        /// 将  Enabled 设置为 true 并且 开始引发 Elapsed 事件。
        /// </summary>
        public void Start()
        {
            if (Interlocked.CompareExchange(ref enabled, 1, 0) == 0) Start(DueTime);
        }

        private void Start(int Interval)
        {
            var time = Time;
            if (time == null) return;
            if (time.Interval != Interval) time.Interval = Interval;
            time.Start();
        }

        /// <summary>
        ///将 Enabled 设置为 false 并且 停止引发 Elapsed 事件。
        /// </summary>
        public void Stop()
        {
            var time = Time;
            if (time == null || Interlocked.CompareExchange(ref enabled, 0, 1) != 1) return;
            time.Stop();
        }

        /// <summary>
        /// 释放所有资源。
        /// </summary>
        public void Dispose()
        {
            var time = Time;
            Time = null;
            if (time == null || Interlocked.Exchange(ref enabled, -1) == -1) return;
            time.Stop();
            time.Dispose();
        }

        /// <summary>
        /// 定时获取时间
        /// </summary>
        private static TimerEx NowTimer;

        /// <summary>
        /// 初始化用的锁
        /// </summary>
        private static readonly object NowLock = new object();

        /// <summary>
        /// 记录的时间
        /// </summary>
        private static DateTime now;

        /// <summary>
        /// 当前时间。定时读取系统时间，避免频繁读取系统时间造成性能瓶颈
        /// </summary>
        public static DateTime Now
        {
            get
            {
                if (NowTimer == null)
                {
                    lock (NowLock)
                    {
                        if (NowTimer == null)
                        {
                            now = DateTime.Now;
                            NowTimer = new TimerEx(500, 500);
                            NowTimer.Elapsed += (a) => CopyNow();
                            //NowTimer.Start();
                        }
                    }
                }
                return now;
            }
        }

        /// <summary>
        /// 更新时间
        /// </summary>
        private static void CopyNow() => now = DateTime.Now;
    }
}